<?php
declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://doc.hyperf.io
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
 */

namespace App\Controller\Websocket;

use Hyperf\Contract\OnCloseInterface;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Swoole\Http\Request;
use Swoole\Server;
use Swoole\Websocket\Frame;
use Swoole\WebSocket\Server as WebSocketServer;
use Hyperf\Guzzle\HandlerStackFactory;
use GuzzleHttp\Client;
use Hyperf\Redis\RedisFactory;
use Hyperf\Utils\ApplicationContext;
use Psr\Http\Message\UriInterface;
use Hyperf\Redis\Redis;
use Hyperf\Config\Config;

/**
*使用到rediswsFd
**/ 
class Index   implements OnMessageInterface, OnOpenInterface, OnCloseInterface
{
    // 在一个心跳周期内，单ip最多可以连接次数。超出次数不能连接
    protected $ipMaxOpen=1000;
    // 异常ip保存时间、7天
    protected $abnormalIpExpire=604800;
    // 心跳时长
    protected $xintiaoExpire=60;
    /**
     * 发送消息
     * @param WebSocketServer $server
     * @param Frame $frame
     */
    public function onMessage(WebSocketServer $server, Frame $frame): void
    {
        //心跳刷新缓存
        $container= ApplicationContext::getContainer();
        $redis = $container->get(RedisFactory::class)->get('wsFd');

        $uid=$redis->get(''.$frame->fd);
        if ($uid) {
            $redis->expire(''.$frame->fd, $this->xintiaoExpire);

            $redis= $container->get(RedisFactory::class)->get('wsUid');
            $listUid=$redis->keys('*');
            // var_dump($uid);
            $fdList=$redis->smembers(''.$uid);
            // var_dump($list);
            //如果当前客户端在客户端集合中,就刷新
            if (in_array($frame->fd, $fdList)) {
                $redis->expire(''.$uid, $this->xintiaoExpire);
            }
            // $server->push($frame->fd, json_encode(['userlist'=>$listUid,'fdlist'=>$fdList]));
        }
    }

    /**
     * 客户端失去链接
     * @param Server $server
     * @param int $fd
     * @param int $reactorId
     */
    public function onClose(Server $server, int $fd, int $reactorId): void
    {

        $container= ApplicationContext::getContainer();
        $redis = $container->get(RedisFactory::class)->get('wsFd');

        $uid=$redis->get(''.$fd);
        if ($uid) {
                $redis->select(2);
                $list=$redis->smembers(''.$uid);
                // var_dump($list);
                if (is_array($list)) {
                   foreach ($list as $i => $fda) {
                    if($fd==$fda){
                        //移除集合中指定的fd
                         $redis->sRem(''.$uid, $fda);
                    }
                   }
                }
        }
        var_dump('closed');
    }

    /**
     * 客户端链接
     * @param WebSocketServer $server
     * @param Request $request
     */
    public function onOpen(WebSocketServer $server, Request $request): void
    {
        // nginx 转发后，remoteip 就是ngix的ip。
        // $remote_ip=$server->getClientInfo($request->fd)['remote_ip'];
        
        $clientInfo=$request->get;
        // var_dump($remote_ip);
        // header里的ip是用户的真实ip
        $remote_ip=$request->header['x-real-ip']??$server->getClientInfo($request->fd)['remote_ip']??null;
        // var_dump([$server->getClientInfo($request->fd),$request]);
        // $fip=$request->header['x-real-ip'];
        $isLogin=null;
        if(isset($clientInfo['token'])){
                $container= ApplicationContext::getContainer();
                $redisIp = $container->get(RedisFactory::class)->get('clientIp');
                $redisIp->lpush($remote_ip,time());
                $redisIp->expire($remote_ip, $this->xintiaoExpire);
                $llen=$redisIp->llen($remote_ip);
                if($llen>=$this->ipMaxOpen){
                            // 删除超出10条的多余数据
                            $redisIp->ltrim($remote_ip, 1, 10);
                            
                            // 把异常ip保存下来
                            $redisabnormalIp = $container->get(RedisFactory::class)->get('abnormalIp');
                            $redisabnormalIp->lpush($remote_ip,time());
                            $redisabnormalIp->expire($remote_ip, $this->abnormalIpExpire);
                            // var_dump($redisabnormalIp->get('ip_'.$remote_ip));
                }else{
                                           
                                $redisdefault = $container->get(RedisFactory::class)->get('default');
                                // 读取缓存库的登录信息
                                $result=$redisdefault->get($clientInfo['token']);
                            // 读取用户缓存
                                //  var_dump($result);
                            if ($result) {
                                $result=json_decode($result);
                                if (is_array($result) && isset($result['uid'])) {
                                    $isLogin=true;
                                    //缓存客户端id 
                                    $redisdefault->set($clientInfo['token'],json_encode($result),$this->xintiaoExpire);
                                    $redis = $container->get(RedisFactory::class)->get('wsFd');
                
                                    $redis->set(''.$request->fd,$result['uid'],$this->xintiaoExpire);
                                    // var_dump($redis->keys('*'));
                                    $redis=$container->get(RedisFactory::class)->get('wsUid');
                                    // var_dump($redis->keys('*'));
                                    $redis->sAdd(''.$result['uid'],$request->fd);
                                    $redis->expire(''.$result['uid'], $this->xintiaoExpire);
                                }
                            }
                            // 如果缓存没有有效的登录信息，连接远程获取
                            if (!$isLogin) {
                                    // 创建http连接 获得用户登录授权
                                    $factory = new HandlerStackFactory();
                                    $stack = $factory->create();
                                    $apiConfig=config('api.getUid');
                                    $client = make(Client::class, [
                                        'config' => array_merge($apiConfig['config'],['handler' => $stack])
                                    ]);
            
                                    $query=array_merge($apiConfig['params'],$clientInfo);
                                   
                                    try{
                                        
                                        $response = $client->request($apiConfig['config']['method']??"GET", $apiConfig['config']['base_uri'].$apiConfig['route']??'',['query' =>$query??[]]);
                                        $userData=$response->getBody()->getContents();
                                        $userData=json_decode($userData,true);
                                    }catch(\Exception $e){
                                        $userData=[];
                                    }
                                    
                                if(isset($userData['result']) && is_array($userData['result']) && isset($userData['result']['uid'])){
                                    $isLogin=true;
                                    $result=$userData['result'];
                
                                    //保存客户端id 到缓存库
                                    $redisdefault->set($clientInfo['token'],json_encode($result),$result['expire']??300);
                                    $redis = $container->get(RedisFactory::class)->get('wsFd');
                                    $redis->set(''.$request->fd,$result['uid'],$this->xintiaoExpire);
                                    // var_dump($redis->keys('*'));
                                    $redis=$container->get(RedisFactory::class)->get('wsUid');
                                    // var_dump($redis->keys('*'));
                                    $redis->sAdd(''.$result['uid'],$request->fd);
                                    $redis->expire(''.$result['uid'], $this->xintiaoExpire);
                                }
                            }
                }
                // echo($llen);

            // $server->push($request->fd,json_encode($userData['result'],JSON_UNESCAPED_UNICODE));
            
        }
        if (!$isLogin) {
            $server->disconnect($request->fd,401, "连接已断开，请先登录");
        }else{
            $server->push($request->fd,json_encode(['message'=>'成功','code'=>0,'clientInfo'=>['client'=>$server->getClientInfo($request->fd)['remote_ip'], 'fd'=>$request->fd,'host'=>$request->header['host']]],JSON_UNESCAPED_UNICODE));
        }
    }

}

